

#ifndef QVRingBuffer_hpp
#define QVRingBuffer_hpp

#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
#include <string.h>

class QMutex{
public:
    pthread_mutex_t *mMutex;
    QMutex(pthread_mutex_t* mutex){
        mMutex = mutex;
        pthread_mutex_lock(mMutex);
    }
    ~QMutex(){
        pthread_mutex_unlock(mMutex);
    }
};

class QVRingBuffer {
public:
    QVRingBuffer(size_t size) {
        mSize = size;
        mData = new char[size];
        
        mHead = mTail = 0;
        mFlag = false;
        pthread_mutex_init(&mMutex, NULL);
        
    }
    
    ~QVRingBuffer() {
        delete  mData;
        pthread_mutex_destroy(&mMutex);
    }
    
    size_t size() { return mSize; }
    
    size_t used() {
        
        QMutex mutex(&mMutex);
        
        if((mFlag == true) && (mTail == mHead))
            return mSize;
        
        return mTail >= mHead?(mTail - mHead):(mSize + mTail - mHead);
    }
    
    size_t free() {
        
        QMutex mutex(&mMutex);
        
        if ((mFlag == false) && (mTail == mHead))
            return mSize;
        
        return  mSize - (mTail > mHead?(mTail - mHead):(mSize + mTail - mHead));
    }
    
    bool write( void * data, size_t len ) {
        
        size_t avail = free();
        
        if ((len > avail) || (data == nullptr) || (len == 0)) {
            return false;
        }
        
        QMutex mutex(&mMutex);
        
        if(mTail + len <= mSize) {
            memcpy(mData + mTail, data, len);

        } else {
            size_t delta = (mTail + len) - mSize;
            
            memcpy(mData + mTail, data, mSize - mTail);
            memcpy(mData, (char *)data + (mSize - mTail), delta);
        }
        
        mTail = (mTail + len) % mSize;
        
        if(mTail == mHead)
            mFlag = true;
        
        return true;
    }
    
    bool read(void * data, size_t len) {
        
        size_t avail = used();
        
        if((len > avail) || data == nullptr)
            return false;
        
        QMutex mutex(&mMutex);
        
        if(mHead + len <= mSize) {
            memcpy(data, mData + mHead, len);
            
        } else {
            size_t delta = (mHead + len) - mSize;
            
            memcpy(data, mData + mHead, mSize - mHead);
            memcpy((char *)data + (mSize - mHead), mData, delta);
        }
        
        mHead = (mHead + len) % mSize;
        
        if(mTail == mHead)
            mFlag = false;
        
        return true;
    }
    
private:
    size_t mSize;
    char * mData;
    pthread_mutex_t mMutex;
    size_t mHead, mTail;
    bool mFlag;
    
};

#endif /* QVRingBuffer_hpp */
